package com.yc.reflect;


//import lombok.Data;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>反射类测试</>
 * 1.反射是Java特有的机制，有自省特性。不能够预知未来，但能管理未来
 * 2.应用场景主要构建对象和方法的调用，很多框架中均是用反射实现。
 * 3.反射对象获取方式
 * 4.反射的核心api: Constructor、Method、 Field、 Annotation
 * 5.demo 获取反射对象、 设置属性值、 获取反射反省参数
 *
 * @author yc112
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ReflectAnnoDemo {

    String value() default "";
}

//@Data
@ReflectAnnoDemo("config")
class ReflectDemo1 {

    private static final String NAME = "ZHANG SAN";
    public String field1;
    /**
     * 泛型测试
     **/
    public List<String> listType;
    protected String field3;
    private String field2;

    public ReflectDemo1() {
    }

    public ReflectDemo1(String arg1) {

    }

    public Map<String, Object> getMapType(List<Integer> integers, List<String> strings) throws ClassNotFoundException {

        return null;
    }

    @Override
    public String toString() {
        return "ReflectDemo1{" +
                "field1='" + field1 + '\'' +
                ", field2='" + field2 + '\'' +
                ", field3='" + field3 + '\'' +
                '}';
    }
}

public class ReflectTest1 {


    public static void main(String[] args) throws Exception {
        // getClassInstance();
        // 通过反射构建实例
//        doInstance1();
//        doInstance2();
//        TestField();
//        Boolean.valueOf();
//        Integer.valueOf();
//        Double.valueOf();

//        Long.valueOf();
        getType();
    }

    /**
     * 获取参数方法的泛型类型
     *
     * @throws NoSuchFieldException
     * @throws NoSuchMethodException
     */
    public static void getType() throws NoSuchFieldException, NoSuchMethodException {
        Class clz1 = ReflectDemo1.class;
        Field field = clz1.getDeclaredField("listType");
        Type t = field.getGenericType();
        System.out.println(t.getTypeName()); // java.util.List<java.lang.String>

        // 方法泛型
        Method getMapType = clz1.getDeclaredMethod("getMapType", List.class, List.class);
        // 方法参数泛型
        Type[] genericParameterTypes = getMapType.getGenericParameterTypes();
        for (Type type : genericParameterTypes) {
            // java.util.List<java.lang.Integer>
            // java.util.List<java.lang.String>
            System.out.println(type.getTypeName());
        }
        // 方法返回值泛型
        Type genericReturnType = getMapType.getGenericReturnType();
        System.out.println(genericReturnType.getTypeName()); // ClassNotFoundException

        // 异常泛型
        Type[] genericExceptionTypes = getMapType.getGenericExceptionTypes();
        for (Type typeException :
                genericExceptionTypes) {
            System.out.println(typeException.getTypeName()); //ClassNotFoundException
        }
    }

    /**
     * 模拟spring 注解注册实例bean
     *
     * @throws Exception
     */
    private static void doInstance2() throws Exception {
        //
//        Class clz1 = ReflectDemo1.class;
        Class clz1 = null;
        try {
            clz1 = Class.forName("com.yc.reflect.ReflectDemo1");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 判断是否有指定注解
        boolean hasAnnotation = clz1.isAnnotationPresent(ReflectAnnoDemo.class);

        if (!hasAnnotation) return;
        // 若存在注解，则给与实例化对象
        // 实例化
        Object obj = clz1.newInstance();

        String value = null;
        Annotation reflectAnnoDemo = clz1.getAnnotation(ReflectAnnoDemo.class);
        if (reflectAnnoDemo instanceof ReflectAnnoDemo) {
            value = ((ReflectAnnoDemo) reflectAnnoDemo).value();
        }


        Map<String, Object> map = new HashMap<>();
        map.put(value, obj);

        System.out.println(map);
//        ReflectDemo1 rdInstance = null;
//        try {
//            Object obj = con.newInstance(ReflectDemo1.class);
//        } catch (InstantiationException e) {
//            e.printStackTrace();
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (InvocationTargetException e) {
//            e.printStackTrace();
//        }


//        ReflectAnnoDemo anno = clz1.getAnnotation(ReflectAnnoDemo.class);
    }

    /**
     * 通过反射获取属性，属性值
     * 同理获取方法
     *
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws NoSuchFieldException
     */
    private static void TestField() throws
            InstantiationException,
            IllegalAccessException,
            NoSuchMethodException,
            NoSuchFieldException,
            InvocationTargetException {
        Class clz1 = ReflectDemo1.class;
        Object obj = clz1.newInstance();

        // 若存在注解，则给与实例化对象
        Constructor con = clz1.getDeclaredConstructor();

        // 获取属性
        con.setAccessible(true);
        // 获取所有属性
        Field[] delareFieldsArr = clz1.getDeclaredFields();
        for (Field field : delareFieldsArr) {
            field.setAccessible(true); // 此处若不设置，则私有属性值无法被访问
            System.out.println("属性名为：" + field.getName() + "属性值：" + field.get(obj));
        }
        // 获取指定属性
        Field declareield2 = clz1.getDeclaredField("field2");
        System.out.println(declareield2.getName());

//        Field privateField2 = clz1.getField("field2");
//        privateField2.setAccessible(true);   // 若不设置，则会出现NoSuchFieldException
//        System.out.println(privateField2.getName());

        // 获取public 属性
        Field[] fieldArr = clz1.getFields();
        AccessibleObject.setAccessible(fieldArr, true);
        for (Field field : fieldArr) {
            System.out.println(field.getName());
        }

        // 获取方法 与获取属性一致，在公共方法和私有方法上依然是有不同的区别、
        Method[] methodArr = clz1.getMethods();
        for (Method method : methodArr) {
            System.out.println(method.getName());
        }

        Method[] declaredMethods = clz1.getDeclaredMethods();
        for (Method declareMethod : declaredMethods) {
            System.out.println(declareMethod.getName());
        }

        Method method = clz1.getDeclaredMethod("setField1", String.class);
        method.invoke(obj, "field1 value");
        System.out.println(obj);
    }

    private static void doInstance1() {
        // Class.forName(类名全路径);
        String className = "com.yc.reflect.ReflectDemo1";
        Class clz3 = null;
        try {
            clz3 = Class.forName(className);
        } catch (ClassNotFoundException e) {

        }
        Constructor con = null;
        try {
            con = clz3.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            System.err.println("没有对应的无参构造函数");
        }

        try {
            con = clz3.getDeclaredConstructor(String.class);
        } catch (NoSuchMethodException e) {
            System.err.println("没有对应的构造函数");
        }
        System.out.println(con);
    }


    /**
     * 获取Class对象方式
     private static void getClassInstance() {
     // 获取反射对象
     // 直接类名.class
     Class clz1 = ReflectDemo1.class;

     // 实例.getClass()
     ReflectDemo1 rd1 = new ReflectDemo1();
     Class clz2 = rd1.getClass();

     // Class.forName(类名全路径);
     String className = "com.yc.reflect.ReflectDemo1";
     Class clz3 = null;
     try {
     clz3 = Class.forName(className);
     } catch (ClassNotFoundException e) {
     System.err.println(className);
     }

     System.out.println(clz1 == clz2);
     System.out.println(clz1.equals(clz2));

     System.out.println(clz1 == clz3);
     System.out.println(clz1.equals(clz3));
     }
     */

}
